這次講的是 attackdefense.com 這個 wargame 平台上面的逆向裡的靜態分析
這平台總共有四題,但我覺得其實難度都一樣,題型也大同小異,
今天會講第一題,會講比較細,後面講其他題就可以快速帶過去了
這次講的是第一個 "Recover Passcode",可以先簡單看一下題目敘述,
大概意思是說可以用 gdb 去看他的組合語言,但 gdb 無法 run ,也就是
他禁止我們做動態分析這樣
首先用 gdb 看組合語言:
$ gdb -q ./challenge
$ set disassembly-flavor intel
$ disass main
這樣可以得到以下的資訊:
Dump of assembler code for function main:
0x0000000000400d2e <+0>: push rbp
0x0000000000400d2f <+1>: mov rbp,rsp
0x0000000000400d32 <+4>: sub rsp,0x30
0x0000000000400d36 <+8>: mov DWORD PTR [rbp-0x24],edi
0x0000000000400d39 <+11>: mov QWORD PTR [rbp-0x30],rsi
0x0000000000400d3d <+15>: mov rax,QWORD PTR fs:0x28
0x0000000000400d46 <+24>: mov QWORD PTR [rbp-0x8],rax # save canary
0x0000000000400d4a <+28>: xor eax,eax
0x0000000000400d4c <+30>: mov QWORD PTR [rbp-0x15],0x0
0x0000000000400d54 <+38>: mov DWORD PTR [rbp-0xd],0x0
0x0000000000400d5b <+45>: mov BYTE PTR [rbp-0x9],0x0
0x0000000000400d5f <+49>: cmp DWORD PTR [rbp-0x24],0x1 # [rbp-0x24] = argc (number)
0x0000000000400d63 <+53>: jg 0x400d78 <main+74> # if no args then exit
0x0000000000400d65 <+55>: lea rdi,[rip+0xb05dc] # 0x4b1348, "\nEnter password as command line argument\n\ni.e. challenge <password> "
0x0000000000400d6c <+62>: call 0x411b80 <puts>
0x0000000000400d71 <+67>: mov eax,0x1
0x0000000000400d76 <+72>: jmp 0x400df5 <main+199> # jump to exit
0x0000000000400d78 <+74>: mov eax,DWORD PTR [rbp-0x24] # eax = argc
0x0000000000400d7b <+77>: cdqe # let edx = 0
0x0000000000400d7d <+79>: shl rax,0x3 # rax = rax << 3, rax = 16
0x0000000000400d81 <+83>: lea rdx,[rax-0x8] # rdx = 8
0x0000000000400d85 <+87>: mov rax,QWORD PTR [rbp-0x30] # [rbp-0x30] is argv (set args xxxx)
0x0000000000400d89 <+91>: add rax,rdx # argv + 0x8
0x0000000000400d8c <+94>: mov rcx,QWORD PTR [rax]
0x0000000000400d8f <+97>: lea rax,[rbp-0x15]
0x0000000000400d93 <+101>: mov edx,0xc
0x0000000000400d98 <+106>: mov rsi,rcx
0x0000000000400d9b <+109>: mov rdi,rax
0x0000000000400d9e <+112>: call 0x4004b0 # strncpy([rbp-0x15] , argv + 0x8,12)
0x0000000000400da3 <+117>: lea rax,[rbp-0x15]
0x0000000000400da7 <+121>: mov rdi,rax
0x0000000000400daa <+124>: call 0x400508 # strlen( rbp-0x15 )
0x0000000000400daf <+129>: cmp rax,0x9 # if > 9 then say "Wrong"
0x0000000000400db3 <+133>: ja 0x400de4 <main+182> # puts("Wrong")
0x0000000000400db5 <+135>: lea rax,[rbp-0x15]
0x0000000000400db9 <+139>: mov edx,0x9
0x0000000000400dbe <+144>: lea rsi,[rip+0x2d932b] # 0x6da0f0 <correct_password> , "hardbuteasy"
0x0000000000400dc5 <+151>: mov rdi,rax
0x0000000000400dc8 <+154>: call 0x4004a8 # strncmp(rbp-0x15, "hardbuteasy", 9)
0x0000000000400dcd <+159>: test eax,eax
0x0000000000400dcf <+161>: jne 0x400de4 <main+182> # puts("Wrong")
0x0000000000400dd1 <+163>: lea rdi,[rip+0xb05b8] # 0x4b1390 , "190da373f10ae1afeb51fbc85609f9ce"
0x0000000000400dd8 <+170>: call 0x400cd3 <print_flag>
0x0000000000400ddd <+175>: mov eax,0x0
0x0000000000400de2 <+180>: jmp 0x400df5 <main+199>
0x0000000000400de4 <+182>: lea rdi,[rip+0xb05c6] # 0x4b13b1 , "Wrong"
0x0000000000400deb <+189>: call 0x411b80 <puts>
0x0000000000400df0 <+194>: mov eax,0x1
0x0000000000400df5 <+199>: mov rcx,QWORD PTR [rbp-0x8] # exit ..
0x0000000000400df9 <+203>: xor rcx,QWORD PTR fs:0x28
0x0000000000400e02 <+212>: je 0x400e09 <main+219>
0x0000000000400e04 <+214>: call 0x4512d0 <__stack_chk_fail_local>
0x0000000000400e09 <+219>: leave
0x0000000000400e0a <+220>: ret
End of assembler dump.
上面組語我有先加一些註解,不過函數名稱是我亂寫的,大概意思有對就好
首先我們要知道在 x64 中,函數呼叫大概長相是:
rax = func(rdi, rsi, rdx)
那我們知道 main
的寫法應該是 int main(int argc, char **argv, char **envp)
換句話說
rdi = argc
rsi = argv
所以一開始看組合語言可以知道
[rbp-0x24] 存 argc
[rbp-0x30] 存 argv
所以在 main+49
的位置可以知道是先看 argc 是否大於 1 ,不是就 exit
然後 main+112
那邊函數是我隨便猜的,因為看起來很像,感覺就是
把我們輸入的前 12 bytes 存到 [rbp - 0x15]
,然後往下看
在 main+124
那邊呼叫一個函數,之後和數字 9 比是不是超過,所以猜函數是 strlen
最後在 main+154
那邊把 [rbp-0x15]
和 hardbuteasy
比前 9 個字
所以很明顯就是整個流程是要我們輸入九個字要等於 hardbutea
即可
其實這樣單純看組合語言對我自己來說,蠻好加深印象的